/**
 * 修改记录:
 * 24-05-04     郑朝军
 * 25-09-29     郑朝军     重构文件控制器，整合图片、视频、附件处理逻辑
 * 25-09-09     郑朝军     对接UploadSvr服务，实现真实的临时文件上传功能
 * 25-09-09     郑朝军     抽取WebSocket上传逻辑到WsUploader.js
 */

/**
 * uni.cxapp.FileController
 * ZGIS-文件控制器，处理图片、视频、附件相关业务逻辑
 *
 * @class
 * @memberof    uni.cxapp.FileController
 */
import WsUploader from './WsUploader.js'

class FileController 
{
  /**
   * 构造函数
   * @param {Object} options 配置选项
   */
  constructor(options = {}) 
  {
    // 基础配置
    this.major = options.major
    this.minor = options.minor
    this.entityId = options.entityId
    this.busType = options.busType || '0'
    this.onLine = options.onLine !== false
    
    // 文件相关配置
    this.fileTypes = options.fileTypes || []
    this.userRawTitle = options.userRawTitle || false
    this.saveGallery = options.saveGallery || false
    
    // 水印配置
    this.waterMarkStream = options.waterMarkStream
    this.waterMarkBuilder = options.waterMarkBuilder
    this.waterMarkAlignment = options.waterMarkAlignment || 'topLeft'
    
    // 相机配置
    this.onOpenCamera = options.onOpenCamera
    
    // WebSocket上传配置
    this.enableWebSocket = options.enableWebSocket || false
    this.chunkSize = options.chunkSize || 1024 * 1024 // 1MB
    this.wsUploadUrl = options.wsUploadUrl
    
    // 上传进度监听器数组，支持多个组件监听
    this.uploadProgressListeners = []
    
    // 初始化WebSocket上传器
    if (this.enableWebSocket) 
    {
      this.wsUploader = new WsUploader({
        wsUploadUrl: this.wsUploadUrl,
        chunkSize: this.chunkSize,
        uploadProgress: options.uploadProgress,
        onUploadComplete: options.onUploadComplete,
        onUploadError: options.onUploadError,
        notifyChange: () => this.notifyChange()
      })
    }
    
    // 缩略图配置
    this.enableThumbnail = options.enableThumbnail !== false
    this.thumbnailSize = options.thumbnailSize || { width: 200, height: 200 }
    
    // 业务字段
    this.memo = options.memo
    this.task = options.task
    this.ext = options.ext
    
    // 回调函数
    this.fileAction = options.fileAction
    this.uploadProgress = options.uploadProgress
    this.onUploadComplete = options.onUploadComplete
    this.onUploadError = options.onUploadError
    this.onChange = options.onChange
    
    // 数据存储
    this.fileList = []
    this.mediaUploads = []
    
    // 获取表名
    this.tabName = this.getTabName()
    
    // 初始化
    if (this.entityId) 
    {
      this.queryMedia(this.entityId)
    }
  }

  /**
   * 获取表名
   * @returns {string} 表名
   */
  getTabName() 
  {
    if (this.major && this.minor) 
    {
      return uni.cxapp.entity.queryTabName(this.major, this.minor) || 'unknown_table'
    }
    return 'unknown_table'
  }

  /**
   * 查询媒体文件
   * @param {number} entityId 实体ID
   * @returns {Promise<void>}
   */
  async queryMedia(entityId) 
  {
    try 
    {
      const params = {
        major: this.major,
        minor: this.minor,
        entityid: entityId
      }

      if (!this.showPhoto && !this.showVideo && !this.showAttach) return
      if (this.busType !== '0') 
      {
        params.bustype = this.busType
      }
      
      if (this.memo) 
      {
        params.memo = this.memo
      }
      
      if (this.task) 
      {
        params.task = this.task
      }
      
      const result = await uni.cxapp.DataSvr.query(uni.cxapp.CxConst.SYSTAB, uni.cxapp.CxConst.TAB_MEDIA, 
      {
        args: Object.entries(params).map(([key, value]) => [key, '=', value, 'and']),
        orderby: 'id asc'
      })

      this.fileList = []
      
      for (const item of result) 
      {
        const mediaType = this.getMediaType(item.filename)
        
        // 过滤文件类型
        if (this.fileTypes.length > 0 && !this.fileTypes.includes(mediaType)) 
        {
          continue
        }

        const fileBean = 
        {
          title: item.title,
          busType: item.bustype ? item.bustype.toString() : '0',
          task: item.task,
          memo: item.memo,
          url: this.onLine ? uni.cxapp.DocGetSvr.getDownloadUrl({ id: item.id }) : item.filename,
          thumbFunc: this.enableThumbnail && this.needsThumbnail(mediaType) ? uni.cxapp.DocGetSvr.downloadThumbnail(item.id) : null,
          netId: item.id,
          mediaType: mediaType,
          preView: item.preview,
          progress: 1.0
        }
        
        this.fileList.push(fileBean)
      }
      
      this.notifyChange()
    }
    catch (error) 
    {
      console.error('查询媒体文件失败:', error)
      throw error
    }
  }

  /**
   * 获取媒体类型
   * @param {string} filename 文件名
   * @returns {string} 媒体类型
   */
  getMediaType(filename) 
  {
    if (!filename) return 'UNKNOWN'
    
    const ext = filename.toLowerCase().split('.').pop()
    
    const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
    const videoExts = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm']
    const docExts = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt']
    
    if (imageExts.includes(ext)) return 'PIC'
    if (videoExts.includes(ext)) return 'VIDEO'
    if (docExts.includes(ext)) return 'DOC'
    
    return 'UNKNOWN'
  }

  /**
   * 是否需要缩略图
   * @param {string} mediaType 媒体类型
   * @returns {boolean} 是否需要缩略图
   */
  needsThumbnail(mediaType) 
  {
    return ['PIC', 'DOC'].includes(mediaType)
  }
  
  /**
   * 删除文件
   * @param {Object} fileBean 文件对象
   * @param {Object} attMap 属性映射
   * @returns {Promise<void>}
   */
  async delFile(fileBean, attMap = {}) 
  {
    try 
    {
      if (fileBean.netId > 0) 
      {
        // 删除服务器文件
        await uni.cxapp.DataSvr.del(uni.cxapp.CxConst.SYSTAB, uni.cxapp.CxConst.TAB_MEDIA, fileBean.netId)
        
        // 从列表中移除
        const index = this.fileList.findIndex(f => f.netId === fileBean.netId)
        if (index > -1) 
        {
          this.fileList.splice(index, 1)
        }
      }
      else 
      {
        // 删除临时上传文件
        this.mediaUploads = this.mediaUploads.filter(item => item.url !== fileBean.url)
        
        // 从列表中移除
        const index = this.fileList.findIndex(f => f.url === fileBean.url)
        if (index > -1) 
        {
          this.fileList.splice(index, 1)
        }
      }
      
      // 更新媒体编码
      this.setMediaCode(attMap, fileBean.busType)
      
      // 触发回调
      if (this.fileAction) 
      {
        this.fileAction()
      }
      
      this.notifyChange()
      this.showSuccessMsg('删除成功')
    }
    catch (error) 
    {
      console.error('删除文件失败:', error)
      this.showErrorMsg('删除失败: ' + error.message)
      throw error
    }
  }

  /**
   * 获取图片列表
   * @param {string} busType 业务类型
   * @returns {Array} 图片列表
   */
  getPhotoList(busType) 
  {
    return this.fileList.filter(file => 
      file.mediaType === 'PIC' && 
      (file.busType === busType || file.busType === '')
    )
  }

  /**
   * 获取视频列表
   * @param {string} busType 业务类型
   * @returns {Array} 视频列表
   */
  getVideoList(busType) 
  {
    return this.fileList.filter(file => 
      file.mediaType === 'VIDEO' && 
      (file.busType === busType || file.busType === '')
    )
  }

  /**
   * 获取附件列表
   * @param {string} busType 业务类型
   * @returns {Array} 附件列表
   */
  getAttachList(busType) 
  {
    return this.fileList.filter(file => 
      (file.busType === busType || file.busType === '')
    )
  }

  /**
   * 上传文件
   * @param {number} entityId 实体ID
   * @param {Object} fileBean 文件对象
   * @param {Function} onSuccess 成功回调
   * @param {Object} attMap 属性映射
   * @returns {Promise<void>}
   */
  async uploadFile(entityId, fileBean, onSuccess, attMap = {}) 
  {
    try 
    {
      // 触发文件操作回调
      if (this.fileAction) 
      {
        this.fileAction()
      }

      // 离线模式处理
      if (!this.onLine) 
      {
        await this.addOffLineMedia(entityId)
        return
      }

      // 更新媒体编码
      this.setMediaCode(attMap, fileBean.busType)

      // 选择上传方式
      if (this.enableWebSocket && this.wsUploader) 
      {
        await this.wsUploader.uploadFileViaWebSocket(entityId, fileBean, onSuccess, this)
      }
      else 
      {
        await this.uploadFileViaHttp(entityId, fileBean, onSuccess)
      }
    }
    catch (error) 
    {
      console.error('上传文件失败:', error)
      this.showErrorMsg('上传失败: ' + error.message)
      
      if (this.onUploadError) 
      {
        this.onUploadError(fileBean, error)
      }
      
      throw error
    }
  }

  /**
   * 通过HTTP上传文件
   * @param {number} entityId 实体ID
   * @param {Object} fileBean 文件对象
   * @param {Function} onSuccess 成功回调
   * @returns {Promise<void>}
   */
  async uploadFileViaHttp(entityId, fileBean, onSuccess) 
  {
    try 
    {
      // 上传进度
      const result = await this.uploadToTemp(fileBean.url, (progress) => 
      {
        fileBean.progress = progress
        
        // 使用新的通知机制
        this.notifyUploadProgress(fileBean, progress)
        
        this.notifyChange()
      })

      // 处理上传结果
      await this.uploadBack(result, entityId, fileBean, onSuccess)
    }
    catch (error) 
    {
      console.error('HTTP上传失败:', error)
      throw error
    }
  }

  /**
   * 上传到临时目录
   * @param {string} filePath 文件路径
   * @param {Function} onProgress 进度回调
   * @returns {Promise<Object>} 上传结果
   */
  async uploadToTemp(filePath, onProgress) 
  {
    try 
    {
      // 调用UploadSvr的uploadToTemp方法
      const result = await uni.cxapp.UploadSvr.uploadToTemp(
        { url: filePath },
        {
          onSendProgress: (res) => 
          {                     
            // uni-app的进度回调res结构：
            // res.totalBytesSent - 已上传字节数
            // res.totalBytesExpectedToSend - 总字节数
            // res.progress - 进度百分比 (0-100)
            
            let progressValue
            if (res.progress !== undefined) 
            {
              // 如果有progress字段，直接使用并转换为0-1范围
              progressValue = res.progress / 100
            }
            else if (res.totalBytesExpectedToSend > 0) 
            {
              // 否则根据字节数计算
              progressValue = res.totalBytesSent / res.totalBytesExpectedToSend
            }
            else 
            {
              progressValue = 0
            }                      
            onProgress(progressValue)
          }
        }
      )
      
      return {
        filename: result.filename,
        length: result.length || 0,
        url: filePath,
        id: result.id
      }
    }
    catch (error) 
    {
      console.error('上传到临时目录失败:', error)
      throw new Error('上传失败: ' + error.message)
    }
  }

  /**
   * 上传回调处理
   * @param {Object} result 上传结果
   * @param {number} entityId 实体ID
   * @param {Object} fileBean 文件对象
   * @param {Function} onSuccess 成功回调
   * @returns {Promise<void>}
   */
  async uploadBack(result, entityId, fileBean, onSuccess) 
  {
    try 
    {
      const suffix = this.getFileSuffix(result.filename)
      
      const uploadData = {
        ...result,
        title: fileBean.userRawTitle ? fileBean.title : `${this.tabName}_${this.mediaUploads.length}.${suffix}`,
        bustype: fileBean.busType,
        url: fileBean.url,
        filename: result.filename.replace(/\\/g, '')
      }
      
      if (fileBean.task) 
      {
        uploadData.task = fileBean.task
      }
      
      if (fileBean.memo) 
      {
        uploadData.memo = fileBean.memo
      }
      
      this.mediaUploads.push(uploadData)
      
      // 自动保存
      if (entityId) 
      {
        this.mediaUploads = [uploadData] // 只保留当前上传的文件
        await this.saveTempFile(entityId, onSuccess)
      }
      
      if (onSuccess) 
      {
        onSuccess(uploadData)
      }
    }
    catch (error) 
    {
      console.error('上传回调处理失败:', error)
      throw error
    }
  }

  /**
   * 获取文件后缀
   * @param {string} filename 文件名
   * @returns {string} 文件后缀
   */
  getFileSuffix(filename) 
  {
    if (!filename) return 'unknown'
    const parts = filename.split('.')
    return parts.length > 1 ? parts.pop() : 'unknown'
  }

  /**
   * 保存临时文件
   * @param {number} entityId 实体ID
   * @param {Function} onSuccess 成功回调
   * @returns {Promise<void>}
   */
  async saveTempFile(entityId, onSuccess) 
  {
    try 
    {
      if (!this.onLine) 
      {
        await this.addOffLineMedia(entityId)
        return
      }
      
      if (this.mediaUploads.length === 0) 
      {
        return
      }

      const filenames = this.mediaUploads.map(item => item.filename).join(',')
      const titles = this.mediaUploads.map(item => item.title).join(',')
      const fileLengths = this.mediaUploads.map(item => item.length || 0).join(',')
      const busTypes = this.mediaUploads.map(item => item.bustype).join(',')
      
      const params = {
        filename: filenames,
        title: titles,
        filelength: fileLengths,
        major: this.major,
        minor: this.minor,
        entityid: entityId,
        uperson: uni.cxapp.UserMethod.getUser().name,
        upate: uni.cxapp.CxDateUtil.getNowDateStrYMD(),
        username: uni.cxapp.UserMethod.getUser().name,
        glid: 0,
        bustype: busTypes
      }
      
      if (this.memo) 
      {
        params.memo = this.mediaUploads.map(item => item.memo || '').join(',')
      }
      
      if (this.task) 
      {
        params.task = this.mediaUploads.map(item => item.task || '').join(',')
      }

      // 调用保存接口
      await uni.cxapp.DocSvr.saveTempFile(params)
      
      // 清空临时上传列表
      this.mediaUploads = []
      
      // 重新查询媒体文件
      await this.queryMedia(entityId)
      
      this.showSuccessMsg('上传成功')
      
      if (onSuccess) 
      {
        onSuccess()
      }
    }
    catch (error) 
    {
      console.error('保存临时文件失败:', error)
      this.showErrorMsg('保存失败: ' + error.message)
      throw error
    }
  }

  /**
   * 添加离线媒体记录
   * @param {number} entityId 实体ID
   * @returns {Promise<void>}
   */
  async addOffLineMedia(entityId) 
  {
    try 
    {
      const mediaList = []
      
      for (const file of this.fileList) 
      {
        if (file.netId <= 0) 
        {
          const item = {
            major: this.major,
            minor: this.minor,
            entityId: entityId,
            filename: file.url,
            title: file.title,
            bustype: file.busType
          }
          
          if (file.task) 
          {
            item.task = file.task
          }
          
          if (file.memo) 
          {
            item.memo = file.memo
          }
          
          mediaList.push(item)
        }
      }
      
      // 去重相同的文件
      const uniqueMediaList = this.removeDuplicatesByKey(mediaList, 'filename')
      
      if (uniqueMediaList.length > 0) 
      {
        await uni.cxapp.DataSvr.addsJson(uni.cxapp.CxConst.SYSTAB, uni.cxapp.CxConst.TAB_MEDIA, uniqueMediaList)
      }
    }
    catch (error) 
    {
      console.error('添加离线媒体记录失败:', error)
      throw error
    }
  }

  /**
   * 根据键去重数组
   * @param {Array} array 数组
   * @param {string} key 键名
   * @returns {Array} 去重后的数组
   */
  removeDuplicatesByKey(array, key) 
  {
    const seen = new Set()
    return array.filter(item => 
    {
      const keyValue = item[key]
      if (seen.has(keyValue)) 
      {
        return false
      }
      seen.add(keyValue)
      return true
    })
  }

  /**
   * 设置媒体编码
   * @param {Object} attMap 属性映射
   * @param {string} colName 列名
   */
  setMediaCode(attMap, colName) 
  {
    if (this.containerRule(colName)) 
    {
      const mediaList = this.fileList.filter(file => file.busType === colName)
      attMap[colName] = mediaList.map(file => file.title).join(',')
      this.notifyChange()
    }
  }

  /**
   * 是否包含编码规则
   * @param {string} colName 列名
   * @returns {boolean} 是否包含规则
   */
  containerRule(colName) 
  {
    // 这里需要根据实际的编码规则管理器实现
    return false
  }

  /**
   * 获取标题
   * @param {Object} attMap 属性映射
   * @param {string} colName 列名
   * @returns {string} 标题
   */
  getTitle(attMap, colName) 
  {
    const mediaList = this.fileList.filter(file => file.busType === colName)
    
    if (this.containerRule(colName)) 
    {
      if (mediaList.length === 0) 
      {
        const context = { ...attMap, seq: '01' }
        // 这里需要根据实际的编码规则管理器实现
        return `${this.tabName}_01`
      }
      else 
      {
        mediaList.sort((a, b) => a.netId - b.netId)
        return this.getNextLetterNumber(mediaList[mediaList.length - 1].title)
      }
    }
    
    return null
  }

  /**
   * 获取下一个字母数字
   * @param {string} lastTitle 最后一个标题
   * @returns {string} 下一个标题
   */
  getNextLetterNumber(lastTitle) 
  {
    // 简单实现，实际需要根据业务规则
    const match = lastTitle.match(/(\d+)$/)
    if (match) 
    {
      const num = parseInt(match[1]) + 1
      return lastTitle.replace(/\d+$/, num.toString().padStart(match[1].length, '0'))
    }
    return lastTitle + '_01'
  }

  /**
   * 显示成功消息
   * @param {string} message 消息内容
   */
  showSuccessMsg(message) 
  {
    if (uni.cxapp.Toast) 
    {
      uni.cxapp.Toast(message)
    }
    else 
    {
      console.log('成功:', message)
    }
  }

  /**
   * 显示错误消息
   * @param {string} message 消息内容
   */
  showErrorMsg(message) 
  {
    if (uni.cxapp.Toast) 
    {
      uni.cxapp.Toast(message)
    }
    else 
    {
      console.error('错误:', message)
    }
  }

  /**
   * 通知变化
   */
  notifyChange() 
  {
    // 这里可以触发事件或回调通知外部组件更新
    if (this.onChange) 
    {
      this.onChange(this.fileList)
    }
  }

  /**
   * 清空文件列表
   */
  clear() 
  {
    this.fileList = []
    this.mediaUploads = []
    this.notifyChange()
  }

  /**
   * 销毁控制器
   */
  destroy() 
  {
    this.clear()
    this.fileAction = null
    this.uploadProgress = null
    this.onUploadComplete = null
    this.onUploadError = null
    this.onChange = null
    
    // 销毁WebSocket上传器
    if (this.wsUploader) 
    {
      this.wsUploader.destroy()
      this.wsUploader = null
    }
  }

  /**
   * 添加上传进度监听器
   * @param {Function} listener 监听器函数
   */
  addUploadProgressListener(listener) 
  {
    if (typeof listener === 'function') 
    {
      this.uploadProgressListeners.push(listener)
    }
  }

  /**
   * 移除上传进度监听器
   * @param {Function} listener 监听器函数
   */
  removeUploadProgressListener(listener) 
  {
      const index = this.uploadProgressListeners.indexOf(listener)
      if (index > -1) 
      {
        this.uploadProgressListeners.splice(index, 1)
      }
  }

  /**
   * 通知所有监听器上传进度更新
   * @param {Object} fileBean 文件对象
   * @param {number} progress 进度值
   */
  notifyUploadProgress(fileBean, progress) 
  {
    // 调用原始的uploadProgress回调（为了向后兼容）
    if (this.uploadProgress) 
    {
      this.uploadProgress(fileBean, progress)
    }
    
    // 通知所有监听器
    this.uploadProgressListeners.forEach(listener => 
    {
      try 
      {
        listener(fileBean, progress)
      } 
      catch (error) 
      {
        console.error('Upload progress listener error:', error)
      }
    })
  }
}

export default FileController